
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8" />
    <meta name="referrer" content="same-origin" />
    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
    <meta name="”applicable-device”" content="”pc,mobile”" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/bootstrap/5.1.0/css/bootstrap.min.css" />
    <link rel="stylesheet"
        href="https://cdn.staticfile.org/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css" />
    <!-- <link rel="stylesheet"
        href="https://fastly.jsdelivr.net/gh/Nomeqc/static@master/js/videojs/nuevo/7.15/skins/videojs.min.css" /> -->
    <link rel="stylesheet"
        href="https://fastly.jsdelivr.net/gh/Nomeqc/static@master/js/videojs/nuevo/7.15/skins/videojs.0.4.min.css" />

    <style type="text/css">
        .logo img {
            height: 32px;
            margin-top: -4px;
        }

        .logo {
            font-family: fantasy;
            color: #e1e1e1 !important;
        }

        body {
            background: #235;
        }

        .nice {
            background: #347 !important;
            color: #9bd;
            border: none;
        }

        .nice-nav {
            background: #123 !important;
        }

        .nice-a {
            color: #bacaff !important;
        }

        .nice-a:hover {
            color: #eee !important;
        }

        input.nice:focus {
            color: #9bd;
        }

        .nice-c {
            color: #ccc;
        }

        .h-list {
            word-break: break-all;
            overflow: hidden;
        }

        .js-play {
            background-color: #123;
        }

        .js-play:hover {
            background-color: #012;
        }

        .ft14 {
            font-size: 14px;
        }
    </style>
    <title>M3U8在线播放</title>
    <link rel="icon" type="image/png" sizes="64x64"
        href="https://static.dingtalk.com/media/lALPJxf-ulPZpJJAQA_64_64.png" />
</head>

<body>
    <script src="https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js"></script>
    <!-- <script type="text/javascript" src="https://fastly.jsdelivr.net/npm/video.js@7.11.5/dist/video.min.js"></script> -->
    <script type="text/javascript" src="https://cdn.staticfile.org/video.js/7.15.4/video.min.js"></script>
    <!-- http-streaming.js 2.0.0之后的版本不支持播放伪装成png的ts文件-->
    <script
        src="https://fastly.jsdelivr.net/npm/@videojs/http-streaming@2.0.0/dist/videojs-http-streaming.min.js"></script>
    <script type="text/javascript"
        src="https://fastly.jsdelivr.net/gh/Nomeqc/static@master/js/videojs/nuevo/7.15/nuevo.min.js"></script>
    <script type="text/javascript" src="https://fastly.jsdelivr.net/gh/Nomeqc/static/js/videojs/lang/zh-CN.js"></script>

    <nav class="navbar navbar-expand-lg navbar-dark shadow-sm rounded nice-nav mb-3">
        <div class="container">
            <a class="navbar-brand logo" href="">
                <img src="https://static.dingtalk.com/media/lALPBF8a6vOtwdtAQA_64_64.png" alt="" class="mr-2" />M3U8播放器
            </a>
        </div>
    </nav>
    <div class="container">
        <div class="row">
            <div class="col">
                <video id="player" class="video-js vjs-16-9 vjs-big-play-centered" controls preload="auto"
                    data-setup="{}" playsinline>
                    <source src="#" type="application/x-mpegURL" />
                    <p class="vjs-no-js">
                        To view this video please enable JavaScript, and consider upgrading to a web browser that
                        <a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
                    </p>
                </video>
                <div class="input-group my-3">
                    <input id="video-url" required name="play" type="text" class="form-control nice"
                        placeholder="M3U8 URL" value="" />
                    <div class="input-group-append">
                        <button id="play-btn" class="btn btn-dark js-play border-0" type="button">
                            <img src="https://static.dingtalk.com/media/lALPDhmOtCc6S2MgIA_32_32.png" height="24" />
                        </button>
                    </div>
                </div>
                <div class="my-3">
                    <div class="d-none history">
                        <ul class="list-unstyled nice-c my-2 h-list text-nowrap"></ul>
                        <a href="javascript:;" class="badge badge-primary clear-history">清空播放记录</a>
                        <a href="javascript:;" class="badge badge-primary js-close-history">关闭列表</a>
                    </div>
                </div>
                <div class="card border-primary mb-3 id-share d-none">
                    <div class="card-body text-primary">
                        <div class="form-group mb-1">
                            <label for="exampleFormControlTextarea1">HTML嵌入视频</label>
                            <textarea class="form-control" id="htmlcode" rows="5"></textarea>
                        </div>
                        <a href="javascript:;" class="badge badge-primary js-copy"
                            data-clipboard-target="#htmlcode">复制代码</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="row nice-c ft14">
            <div class="col">
                <h6 class="font-weight-bold">M3U8文件格式</h6>
                <p>
                    <strong>M3U8</strong>
                    文件是指UTF-8编码格式的M3U文件。M3U文件是记录了一个索引纯文本文件，打开它时播放软件并不是播放它，而是根据它的索引找到对应的音视频文件的网络地址进行在线播放。
                </p>
                <p>
                    <strong>M3U</strong>
                    是一种播放多媒体列表的文件格式，它的设计初衷是为了播放音频文件，比如MP3，但是越来越多的软件现在用来播放视频文件列表，M3U也可以指定在线流媒体音频源。很多播放器和软件都支持M3U文件格式。
                </p>
            </div>
        </div>
    </div>
    <script src="https://cdn.staticfile.org/bootstrap/5.1.0/js/bootstrap.min.js"></script>
    <script>
        let originList, dataList, playlistSorts;
        let randomRecord = [];
        let url = encodeURI(decodeURIComponent(window.location.hash.substring(1)));
        document.querySelector("#video-url").value = (url.startsWith('http://') || url.startsWith('https://')) ? url : "";

        let switchControl = (function () {
            let randomInt = function (min, max) {
                const minValue = Math.ceil(min)
                const maxValue = Math.floor(max)
                const buffer = new Uint32Array(100)
                window.crypto.getRandomValues(buffer)
                const i = Math.floor(Math.random() * 100)
                const randomInt = buffer[i]
                return randomInt % (maxValue - minValue + 1) + minValue;
            }

            let generateRandomItemIndex = () => {
                let indexList = player.playlist().map((val, index) => index).filter(val => !randomRecord.includes(val));
                let i = randomInt(0, indexList.length - 1);
                return indexList[i];
            }

            this.switchPrevious = function (source) {
                if (!dataList) {
                    return;
                }
                if (randomRecord.length >= player.playlist().length) {
                    randomRecord = [];
                }
                let currentIndex = player.playlist.currentIndex();
                let isRandomPlay = getSort() == 3;
                if (isRandomPlay) {
                    let position = randomRecord.indexOf(currentIndex);
                    if (position > 0) {
                        player.playlist.currentItem(randomRecord[position - 1]);
                        return;
                    }
                    let itemIndex = generateRandomItemIndex();
                    randomRecord.unshift(itemIndex);
                    player.playlist.currentItem(itemIndex);
                    return;
                }
                // 如果是自动播放 单曲循环
                if (!source && getSort() == 4) {
                    player.currentTime(0)
                    player.play()
                } else {
                    let i = (playlistSorts.indexOf(currentIndex) - 1 + playlistSorts.length) % playlistSorts.length;
                    player.playlist.currentItem(playlistSorts[i]);
                }
            }
            this.switchNext = function (source) {
                if (!dataList) {
                    return;
                }
                if (randomRecord.length >= player.playlist().length) {
                    randomRecord = [];
                }
                let currentIndex = player.playlist.currentIndex();
                let isRandomPlay = getSort() == 3;
                if (isRandomPlay) {
                    let position = randomRecord.indexOf(currentIndex);
                    if (position > -1 && position < randomRecord.length - 1) {
                        player.playlist.currentItem(randomRecord[position + 1]);
                        return;
                    }
                    let itemIndex = generateRandomItemIndex();
                    randomRecord.push(itemIndex);
                    player.playlist.currentItem(itemIndex);
                    return;
                }
                // 如果是自动播放 单曲循环
                if (!source && getSort() == 4) {
                    player.currentTime(0)
                    player.play()
                } else {
                    let i = (playlistSorts.indexOf(currentIndex) + 1) % playlistSorts.length;
                    player.playlist.currentItem(playlistSorts[i]);
                }

            }
            return this;
        })();

        let scrollToCurrentPlaying = function () {
            let currentIndex = player.playlist.currentIndex();
            let child = document.querySelector(`div.vjs-vlist > div.vjs-item[data-id='${currentIndex}']`);
            let parent = document.querySelector("div > div.vjs-vlist");
            let scrollTop = child.offsetTop - (parent.clientHeight - child.clientHeight) / 2;
            parent.scrollTop = Math.min(Math.max(0, scrollTop), parent.scrollHeight - parent.clientHeight);
        }

        let player = videojs(document.querySelector("video"), {
            autoplay: true,
            language: 'zh-CN'
        });
        player.ready(function () {
            titleScroll();
            this.on("loadedmetadata", function () {
                let currentIndex = player.playlist.currentIndex();
                let title = player.playlist()[currentIndex].title;
                changeTitle(title);
                setCookie('currentItem', encodeURIComponent(title), 3650);
                //处理手动点击列表歌曲，没有添加进随机记录的情况
                if (getSort() == 3) {
                    if (!randomRecord.includes(currentIndex)) {
                        randomRecord.push(currentIndex);
                    }
                }
                setTimeout(() => {
                    scrollToCurrentPlaying();
                }, 400);
            });
            this.on("playlistready", function () {
                localStorage.volume || this.volume(0.5);
                restorePlay(dataList);

                // 播放列表准备完毕，如果是随机播放，添加当前歌曲
                if (getSort() == 3) {
                    randomRecord.push(player.playlist.currentIndex())
                }

                setTimeout(() => {
                    document.querySelector("div.vjs-head > span").textContent = `播放列表(${player.playlist().length})`;

                    let onclick = document.querySelector("#player > div.vjs-playlist-button").onclick;
                    document.querySelector("#player > div.vjs-playlist-button").onclick = function (e) {
                        scrollToCurrentPlaying();
                        onclick(e);
                    }

                    suportSort();

                    dataList.forEach((item, index) => {
                        let datetime = new Date(item.update_time * 1000).format("yyyy-MM-dd hh:mm")
                        document.querySelector(`div.vjs-vlist > div.vjs-item[data-id='${index}']`).setAttribute('title', `上传时间：${datetime}`);
                    })

                    //添加 定位按钮
                    var span = document.createElement('span');
                    span.innerHTML = `<svg t="1611292177924" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1198" width="24" height="24"><path d="M469.333333 850.688A341.504 341.504 0 0 1 173.312 554.666667H106.666667a21.333333 21.333333 0 0 1-21.333334-21.333334v-42.666666a21.333333 21.333333 0 0 1 21.333334-21.333334h66.645333A341.504 341.504 0 0 1 469.333333 173.312V106.666667a21.333333 21.333333 0 0 1 21.333334-21.333334h42.666666a21.333333 21.333333 0 0 1 21.333334 21.333334v66.645333A341.504 341.504 0 0 1 850.688 469.333333H917.333333a21.333333 21.333333 0 0 1 21.333334 21.333334v42.666666a21.333333 21.333333 0 0 1-21.333334 21.333334h-66.645333A341.504 341.504 0 0 1 554.666667 850.688V917.333333a21.333333 21.333333 0 0 1-21.333334 21.333334h-42.666666a21.333333 21.333333 0 0 1-21.333334-21.333334v-66.645333zM512 768a256 256 0 1 0 0-512 256 256 0 0 0 0 512z m0-128a128 128 0 1 1 0-256 128 128 0 0 1 0 256z" p-id="1199" fill="#ffffff"></path></svg>`;
                    span.setAttribute('style', 'padding-left: 12px; cursor: pointer;');
                    span.onclick = function () {
                        let dataId = player.playlist.currentIndex();
                        let itemElement = document.querySelector(`div.vjs-vlist > div.vjs-item[data-id='${dataId}']`);
                        itemElement.scrollIntoViewIfNeeded ? itemElement.scrollIntoViewIfNeeded() : itemElement.scrollIntoView({
                            "block": "center"
                        });
                    }
                    document.querySelector("div.vjs-head").appendChild(span);

                    //添加滚动到顶部按钮
                    var span = document.createElement('span');
                    span.innerHTML = `<svg t="1611649776178" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4806" width="21" height="21"><path d="M516.544 397.248a31.904 31.904 0 0 0-22.624 9.376L86.624 813.92a32 32 0 1 0 45.248 45.248l384.672-384.64 384.672 384.64a32 32 0 0 0 45.248-45.248L539.2 406.624a31.904 31.904 0 0 0-22.624-9.376zM96 192h832a32 32 0 0 1 0 64H96a32 32 0 1 1 0-64z" fill="#ffffff" p-id="4807"></path></svg>`;
                    span.setAttribute('style', 'padding-left: 12px; cursor: pointer;');
                    span.onclick = function () {
                        document.querySelector("div.vjs-vlist > div:nth-child(1)").scrollIntoView({
                            "block": "center"
                        });
                    }
                    document.querySelector("div.vjs-head").appendChild(span);

                    // 优化滚动条样式，不滚动时自动隐藏
                    (function () {
                        let scrollTimer;
                        let ele = document.querySelector(".vjs-vlist")
                        ele.addEventListener("scroll", () => {
                            if (ele.classList.contains('vjs-vlist-scrollbar-hidden')) {
                                ele.classList.remove('vjs-vlist-scrollbar-hidden')
                            }
                            clearTimeout(scrollTimer);
                            scrollTimer = setTimeout(() => {
                                if (!ele.classList.contains('vjs-vlist-scrollbar-hidden')) {
                                    ele.classList.add('vjs-vlist-scrollbar-hidden')
                                }
                            }, 1000);
                        });
                        if (!ele.classList.contains('vjs-vlist-scrollbar-hidden')) {
                            ele.classList.add('vjs-vlist-scrollbar-hidden')
                        }
                    })()

                }, 0);
            });

            this.on('useractive', function () {
                document.querySelector("div.vjs-playlist-button").style.display = "block";
            });
            this.on('userinactive', function () {
                document.querySelector("div.vjs-playlist-button").style.display = "none";
            });

            this.on("ended", function () {
                switchControl.switchNext();
            });
            this.on("error", function () {
                switchControl.switchNext();
            });
        });
        player.nuevo({
            contextUrl: "#",
            contextText: "Fallrainy的m3u8播放器",
            contextIcon: "",
            shareMenu: false,
            zoomMenu: false,
            playlistShow: true,
            // Set false to hide playlistUI when video player ready to show
            playlistAutoHide: true,
            // Set false to keep playlist UI visible when video starts playing
            playlistNavigation: true,
            //Set true to show playlist Next and Previous arrows
            playlistRepeat: true,
            // Set true to play first media file when last media file ended
        });

        function getSort() {
            let sort = parseInt(getCookie('playlist_sort'));
            sort = isNaN(sort) ? 0 : sort % 5;
            return sort;
        }

        function sortList(list) {
            let sort = getSort();
            if (sort == 0) {
                list = originList
            } else if ([1, 2, 3, 4].includes(sort)) {
                list.sort((v1, v2) => {
                    let t1 = v1['update_time'];
                    let t2 = v2['update_time'];
                    return [2, 3, 4].includes(sort) ? t2 - t1 : t1 - t2;
                });
            }
            if (sort == 3) {
                //再次切换到随机后，清除随机记录，只记录当前歌曲
                randomRecord = [];
                if (player.playlist.currentIndex) {
                    randomRecord.push(player.playlist.currentIndex())
                }
            }
            return list;
        }

        function suportSort() {

            document.querySelector("#player > div.vjs-playlist-nav.vjs-nav-prev").onclick = function () {
                switchControl.switchPrevious('click');
            }

            document.querySelector("#player > div.vjs-playlist-nav.vjs-nav-next").onclick = function () {
                switchControl.switchNext('click');
            }

            let genIconHTML = () => {
                let sort = getSort();
                let html = {
                    //自然
                    '0': `<svg t="1634960479807" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15869" width="24" height="24"><path d="M864 844H160v-88h704v88z m0-192H160v-88h704v88z m0-192H160v-88h704v88z m0-192H160V180h704v88z" p-id="15870" fill="#ffffff"></path></svg>`,
                    //升序
                    '1': `<svg t="1634957072684" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5258" width="24" height="24"><path d="M810.666667 128l170.666666 213.333333h-128v512h-85.333333V341.333333h-128l170.666667-213.333333z m-213.333334 640v85.333333H128v-85.333333h469.333333z m0-298.666667v85.333334H128v-85.333334h469.333333z m-85.333333-298.666666v85.333333H128V170.666667h384z" p-id="5259" fill="#ffffff"></path></svg>`,
                    //降序
                    '2': `<svg t="1634957115859" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5557" width="24" height="24"><path d="M853.333333 170.666667v512h128l-170.666666 213.333333-170.666667-213.333333h128V170.666667h85.333333z m-341.333333 597.333333v85.333333H128v-85.333333h384z m85.333333-298.666667v85.333334H128v-85.333334h469.333333z m0-298.666666v85.333333H128V170.666667h469.333333z" p-id="5558" fill="#ffffff"></path></svg>`,
                    //随机
                    '3': `<svg t="1634957254885" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6288" width="24" height="24"><path d="M753.564731 337.471035c-45.8697 0-160.259984 113.849978-243.789399 194.548928C383.134027 654.383848 263.508509 773.284865 167.764911 773.284865l-58.892295 0c-24.068162 0-43.581588-19.526729-43.581588-43.581588s19.513426-43.581588 43.581588-43.581588l58.892295 0c60.504002 0 183.002964-121.68134 281.432741-216.784348 119.79641-115.744117 223.254713-219.029482 304.368102-219.029482l56.209186 0-59.641355-57.828057c-17.033955-16.993023-17.060561-42.902112-0.057305-59.927881 17.002232-17.030885 44.596707-17.064654 61.631686-0.065492l134.207631 133.874033c8.192589 8.172123 12.794397 19.238157 12.794397 30.803563 0 11.564383-4.601808 22.604834-12.794397 30.776957L811.706943 461.72599c-8.505721 8.486278-19.646456 12.522198-30.78719 12.522198-11.166317 0-22.333658-4.676509-30.844495-13.199627-17.003256-17.025769-16.975627-45.432749 0.057305-62.425771l59.641355-61.151755L753.564731 337.471035zM811.706943 561.66105c-17.034978-16.999163-44.629453-16.972557-61.631686 0.058328-17.003256 17.024745-16.975627 46.257533 0.057305 63.250556l59.641355 61.150732-56.209186 0c-35.793204 0-95.590102-52.946886-154.87637-108.373243-17.576307-16.435321-45.161572-16.3422-61.594847 1.226944-16.444531 17.568121-15.523555 46.393633 2.053776 62.823837 90.322122 84.458577 151.246703 131.484613 214.417441 131.484613l56.209186 0-59.641355 57.824987c-17.033955 16.993023-17.060561 43.736107-0.057305 60.761875 8.511861 8.523117 19.678178 12.369725 30.844495 12.369725 11.140735 0 22.281469-4.453429 30.78719-12.939707L945.914574 757.311055c8.192589-8.173147 12.794397-19.315928 12.794397-30.881334 0-11.564383-4.601808-22.682605-12.794397-30.855752L811.706943 561.66105zM108.871593 337.471035l58.892295 0c45.932122 0 114.40154 58.455343 168.915108 107.942431 8.352225 7.576559 18.832927 12.140505 29.29214 12.140505 11.852956 0 23.673166-4.394077 32.270984-13.857613 16.182564-17.807574 14.859429-46.823422-2.958378-62.998823-85.247546-77.381391-156.561755-130.388652-227.519854-130.388652l-58.892295 0c-24.068162 0-43.581588 19.526729-43.581588 43.581588S84.804455 337.471035 108.871593 337.471035z" p-id="6289" fill="#ffffff"></path></svg>`,
                    //单曲循环
                    '4': `<svg t="1642981831307" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1937" width="24" height="24"><path d="M911.788443 228.143992L684.489776 0.859776l-61.296945 61.30417 123.395866 123.38864H89.301065v327.921315h86.700064V272.252649h570.587568L623.192831 395.64129l61.296945 61.30417 227.298667-227.276992-0.758626-0.76585zM783.428999 751.747351H229.133818l123.38864-123.381416-61.30417-61.31862-227.298666 227.284217 0.758625 0.758625-0.758625 0.758626 227.298666 227.291441 61.30417-61.311395-123.38864-123.381415h640.995244V513.871276h-86.700063z" fill="#ffffff" p-id="1938"></path><path d="M445.681676 364.559317h86.700064v294.874141h-86.700064z" fill="#ffffff" p-id="1939"></path></svg>`
                };
                return html[sort + ''];
            }
            let span = document.createElement('span');
            span.innerHTML = genIconHTML();
            span.setAttribute('style', 'padding-left: 16px; cursor: pointer;');
            span.onclick = function () {
                let sort = getSort() + 1 % 5;
                setCookie('playlist_sort', sort + '', 3650);

                this.innerHTML = genIconHTML();

                let list = dataList.concat();
                list = sortList(list);
                playlistSorts = list.map((val, index) => dataList.indexOf(val));

                let parentNode = document.querySelector('div.vjs-vlist');
                for (let dataId of playlistSorts) {
                    parentNode.appendChild(document.querySelector(`div.vjs-vlist > div.vjs-item[data-id='${dataId}']`));
                }
                parentNode.appendChild(document.querySelector('div.vjs-vlist > div.vjs-last'));

                let dataId = player.playlist.currentIndex();
                let itemElement = document.querySelector(`div.vjs-vlist > div.vjs-item[data-id='${dataId}']`);
                itemElement.scrollIntoViewIfNeeded ? itemElement.scrollIntoViewIfNeeded() : itemElement.scrollIntoView({
                    "block": "center"
                });
            };
            document.querySelector("div.vjs-head").appendChild(span);
        }

        function loadList(list) {
            let playlist = [];
            //如果锚点传入了地址，则加入列表头
            if (url.startsWith('http://') || url.startsWith('https://')) {
                list.unshift({
                    'url': url,
                    'name': decodeURIComponent(url.split("/").pop().replace(".m3u8", "")),
                    'update_time': parseInt(new Date().getTime() / 1000)
                });
                removeLocationHash()
                eraseCookie('currentItem');
            }
            originList = list.concat();

            list = sortList(list);
            dataList = list.concat();

            playlistSorts = list.map((val, index) => index);
            playlist = list.map(item => {
                // let pic = `https://fastly.jsdelivr.net/gh/Nomeqc/static/video/MV/cover/${item.name}.jpg`;
                let pic = item.cover_url;
                // pic = encodeURI(pic);
                return {
                    sources: [{
                        src: item.url,
                        type: "application/x-mpegURL",
                    }],
                    thumb: pic,
                    title: item.name,
                    duration: item.duration ? item.duration : ''
                };
            })
            player.playlist(playlist);
        }

        //加载MV列表
        async function requestData() {
            function randomString() {
                const expect = 16;
                let str = Math.random().toString(36).substring(2);
                while (str.length < expect) {
                    str += Math.random().toString(36).substring(2);
                }
                return str.substring(0, expect);
            }
            let raw_url = 'raw.githubusercontent.com/nomeqc/my-mv/master/playlist.json'
            let apis = [`https://pic1.xuehuaimg.com/proxy/https://${randomString()}:${randomString()}@${raw_url}`,
                'https://github.fallrainy.workers.dev/nomeqc/my-mv/raw/master/playlist.json',
                `https://${raw_url}`]
            for (let api of apis) {
                let result = await new Promise(resolve => {
                    $.ajax({
                        url: api,
                        type: 'GET',
                        dataType: 'json',
                        timeout: 1000 * 10,
                        success: function (data) {
                            if (data) {
                                loadList(data);
                                resolve(true);
                            } else {
                                resolve(false);
                            }
                        },
                        error: function (xhr, status, error) {
                            console.log(`${api} 请求出错。`);
                            resolve(false);
                        }
                    });
                });
                if (result) {
                    break;
                }
            }
        }

        requestData();

        function removeLocationHash() {
            if (window.location.href.indexOf('#') == -1) {
                return
            }
            let noHashURL = window.location.href.replace(/#.*$/, '');
            window.history.replaceState('', '', noHashURL)
        }

        window.onhashchange = function (e) {
            let url = window.location.hash.substring(1);
            if (url.startsWith('http://') || url.startsWith('https://')) {
                playNewVideo(url)
            }
            removeLocationHash()
            e.preventDefault();
        }
        
        window.onkeydown = function (e) {
            let ctrl = e.ctrlKey;
            let alt = e.altKey;
            let keyCode = e.keyCode;
            let leftCode = 37, rightCode = 39, enterCode = 13, zeroCode = 96
            if (ctrl && keyCode == leftCode) {
                switchControl.switchPrevious('key')
            }
            if (ctrl && keyCode == rightCode) {
                switchControl.switchNext('key')
            }

            if ((ctrl && keyCode == enterCode)) {
                if (player.paused()) {
                    player.play()
                } else {
                    player.pause()
                }
            }
            if (ctrl && alt && keyCode == zeroCode) {
                player.currentTime(0)
            }
            if (!ctrl && !alt && keyCode == enterCode) {
                playNew()
            }
        }

        document.querySelector("#play-btn").onclick = function () {
            playNew()
        }

        let playNew = function() {
            let videoUrl = document.querySelector("#video-url").value;
            if (videoUrl.startsWith('http://') || videoUrl.startsWith('https://')) {
                playNewVideo(videoUrl)
                document.querySelector("#video-url").value = ''
            }
        }

        function playNewVideo(url) {
            let name = decodeURIComponent(url.split("/").pop().replace(".m3u8", ""))
            let item = {
                sources: [{
                    src: url,
                    type: "application/x-mpegURL",
                }],
                thumb: '',
                title: name,
                duration: ""
            }
            player.playlist.insertBefore(item, 0)
            // 调用currentItem()刷新当前位置，避免切换失败
            player.playlist.currentItem()
            player.playlist.first()
            document.querySelector("div.vjs-head > span").textContent = `播放列表(${player.playlist().length})`;
        }

        /** 滚动标题 **/
        let titleText = document.title;

        function changeTitle(videoName) {
            titleText = `正在播放♪ ${videoName}...`;
        }

        function titleScroll() {
            titleText = titleText.substring(1, titleText.length) + titleText.substring(0, 1);
            document.title = titleText;
            setTimeout("titleScroll()", 400);
        }

        function restorePlay(list) {
            let itemName = decodeURIComponent(getCookie('currentItem'));
            if (itemName == 'null') {
                return;
            }
            let index = Math.max(0, list.findIndex(ele => ele.name == itemName));
            player.playlist.currentItem(index);
        }

        function setCookie(name, value, days) {
            var expires = "";
            if (days) {
                var date = new Date();
                date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
                expires = "; expires=" + date.toUTCString();
            }
            document.cookie = name + "=" + (value || "") + expires + "; path=/";
        }

        function getCookie(name) {
            var nameEQ = name + "=";
            var ca = document.cookie.split(';');
            for (var i = 0; i < ca.length; i++) {
                var c = ca[i];
                while (c.charAt(0) == ' ')
                    c = c.substring(1, c.length);
                if (c.indexOf(nameEQ) == 0)
                    return c.substring(nameEQ.length, c.length);
            }
            return null;
        }

        function eraseCookie(name) {
            document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
        }

        Date.prototype.format = function (fmt) {
            var o = {
                "M+": this.getMonth() + 1, //月份
                "d+": this.getDate(), //日
                "h+": this.getHours(), //小时
                "m+": this.getMinutes(), //分
                "s+": this.getSeconds(), //秒
                "q+": Math.floor((this.getMonth() + 3) / 3), //季度
                "S": this.getMilliseconds() //毫秒
            };
            if (/(y+)/.test(fmt)) {
                fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
            }
            for (var k in o) {
                if (new RegExp("(" + k + ")").test(fmt)) {
                    fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
                }
            }
            return fmt;
        }
    </script>
</body>

</html>